#pragma once
#include "Image.hpp"

namespace zzz{
template<typename T>
class PixelHelper {
public:
  static const T Min;
  static const T Max;
  static inline T Clamp(const T& v)
  {
    return zzz::Clamp<T>(Min, v, Max);
  }
  template<typename T1>
  static inline T Clamp(const T1& v)
  {
    return static_cast<T>(zzz::Clamp<T1>(Min, v, Max));
  }
};

template<typename FROM, typename TO> 
inline void ConvertPixel(const FROM &from, TO &to);

template<typename FROM, typename TO> 
inline TO ConvertPixel(const FROM &from)
{
  TO to;
  ConvertPixel<FROM, TO>(from,to);
  return to;
}

template<typename FROM, typename TO>
void ConvertImage(const Image<FROM> &from, Image<TO> &to)
{
  to.SetSize(from.Size());
  to.orir_=from.orir_;
  to.oric_=from.oric_;
  for (zuint i=0; i<from.size(); i++)
    ConvertPixel<FROM, TO>(from[i],to[i]);
}


//clamp
template<>
inline Vector3f PixelHelper<Vector3f>::Clamp(const Vector3f& v)
{
  Vector3f x=v;
  x[0]=zzz::Clamp<float>(Min[0],x[0],Max[0]);
  x[1]=zzz::Clamp<float>(Min[1],x[1],Max[1]);
  x[2]=zzz::Clamp<float>(Min[2],x[2],Max[2]);
  return x;
}

template<>
inline Vector4f PixelHelper<Vector4f>::Clamp(const Vector4f& v)
{
  Vector4f x=v;
  x[0]=zzz::Clamp<float>(Min[0],x[0],Max[0]);
  x[1]=zzz::Clamp<float>(Min[1],x[1],Max[1]);
  x[2]=zzz::Clamp<float>(Min[2],x[2],Max[2]);
  x[3]=zzz::Clamp<float>(Min[3],x[3],Max[3]);
  return x;
}

template<>
inline Vector3uc PixelHelper<Vector3uc>::Clamp(const Vector3uc& v)
{
  Vector3uc x=v;
  zzz::Clamp<zuchar>(Min[0],x[0],Max[0]);
  zzz::Clamp<zuchar>(Min[1],x[1],Max[1]);
  zzz::Clamp<zuchar>(Min[2],x[2],Max[2]);
  return x;
}

template<>
inline Vector4uc PixelHelper<Vector4uc>::Clamp(const Vector4uc& v)
{
  Vector4uc x=v;
  zzz::Clamp<zuchar>(Min[0],x[0],Max[0]);
  zzz::Clamp<zuchar>(Min[1],x[1],Max[1]);
  zzz::Clamp<zuchar>(Min[2],x[2],Max[2]);
  zzz::Clamp<zuchar>(Min[3],x[3],Max[3]);
  return x;
}

//conversion function
template<>
inline void ConvertPixel<zuchar, float>(const zuchar &v, float &t)
{t=PixelHelper<float>::Clamp(v/255.0f);}

template<>
inline void ConvertPixel<float, zuchar>(const float &v, zuchar &t)
{t=PixelHelper<zuchar>::Clamp(v*255.0f);}

template<>
inline void ConvertPixel<Vector3uc, zuchar>(const Vector3uc &v, zuchar &t)
{t=PixelHelper<zuchar>::Clamp(int((0.212671f *v[0] + 0.715160f * v[1] + 0.072169f * v[2])));}

template<>
inline void ConvertPixel<Vector4uc, zuchar>(const Vector4uc &v, zuchar &t)
{t=PixelHelper<zuchar>::Clamp(int((0.212671f *v[0] + 0.715160f * v[1] + 0.072169f * v[2])));}

template<>
inline void ConvertPixel<Vector3f, float>(const Vector3f &v, float &t)
{t=PixelHelper<float>::Clamp((0.212671f *v[0] + 0.715160f * v[1] + 0.072169f * v[2]));}

template<>
inline void ConvertPixel<Vector4f, float>(const Vector4f &v, float &t)
{t=PixelHelper<float>::Clamp((0.212671f *v[0] + 0.715160f * v[1] + 0.072169f * v[2]));}

template<>
inline void ConvertPixel<Vector3uc, float>(const Vector3uc &v, float &t)
{t=PixelHelper<float>::Clamp(0.212671f * v[0] / 255.0f + 0.715160f * v[1] / 255.0f + 0.072169f * v[2] / 255.0f);}

template<>
inline void ConvertPixel<float, Vector3f>(const float &v, Vector3f &t)
{t=Vector3f(v);}

template<>
inline void ConvertPixel<zuchar, Vector3f>(const zuchar &v, Vector3f &t)
{t=Vector3f(v/255.0f);}

template<>
inline void ConvertPixel<Vector3uc, Vector3f>(const Vector3uc &v, Vector3f &t)
{t=Vector3f(v[0] / 255.0f, v[1] / 255.0f, v[2] / 255.0f);}

template<>
inline void ConvertPixel<Vector3f, Vector3uc>(const Vector3f &v, Vector3uc &t)
{t=Vector3uc(v[0] * 255.0f, v[1] * 255.0f, v[2] * 255.0f);}

template<>
inline void ConvertPixel<Vector3f, Vector4f>(const Vector3f& v, Vector4f &t)
{t=Vector4f(v[0],v[1],v[2],1.0f);}

template<>
inline void ConvertPixel<Vector4f, Vector3f>(const Vector4f& v, Vector3f &t)
{t=Vector3f(v[0],v[1],v[2]);}

template<>
inline void ConvertPixel<Vector4f, Vector4uc>(const Vector4f& v, Vector4uc &t)
{t=Vector4uc(v[0]*255.0f,v[1]*255.0f,v[2]*255.0f,v[3]*255.0f);}

template<>
inline void ConvertPixel<Vector4uc, Vector4f>(const Vector4uc& v, Vector4f &t)
{t=Vector4f(v[0]/255.0f,v[1]/255.0f,v[2]/255.0f,v[3]/255.0f);}

template<>
inline void ConvertPixel<Vector3f, zuchar>(const Vector3f &v, zuchar &t)
{t=PixelHelper<zuchar>::Clamp((0.212671f *v[0] + 0.715160f * v[1] + 0.072169f * v[2])*255);}

} // namespace zzz
